/** @file    wCore_PropertiesProvider.cpp
 *  @author  Alessandro Polo
 *  @version $Id: wCore_PropertiesProvider.cpp 2878 2010-08-09 01:11:12Z alex $
 *  @brief
 * File containing methods for the wosh::gui::wCore_PropertiesProvider class.
 * The header for this class can be found in wCore_PropertiesProvider.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include "wCore_PropertiesProvider.h"
 #include "ui_wCore_PropertiesProvider.h"

 #include <core/Request.h>
 #include <core/Response.h>
 #include <core/MethodsCommon.h>
 #include <core/SecurityManager.h>

 #include <framework/gui/GuiUtilities.h>
 #include <QtGui/QMenu>
 #include <QtGui/QContextMenuEvent>
 #include <QtGui/QInputDialog>
 #include <QtGui/QMessageBox>
 #include <QHeaderView>


using namespace std;
namespace wosh {
 namespace gui {

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

wCore_PropertiesProvider::wCore_PropertiesProvider(QWidget *parent) : IWidgetGeneric(parent)
 {
	this->m_ui = new Ui::wCore_PropertiesProvider();
	this->m_ui->setupUi(this);

	this->myURI.setName( Utilities::format("%s#%10lu", getClassName(), Utilities::randomLong()) );
	this->myURI.setKernelLocal();

	this->menuKey = NULL;
	this->menuValue = NULL;
	this->menuPerm = NULL;

	GenericItemDelegate* tblDelegate = new GenericItemDelegate(this);
	this->m_ui->tableView->getTableView()->setItemDelegate(tblDelegate);

	this->m_ui->wUpBarProps->setEnabled(false);

	connect(this->m_ui->tableView, SIGNAL(tableCell_changed(unsigned int,unsigned int, ISerializable*)), this, SLOT(cell_valueChanged(unsigned int,unsigned int, ISerializable*)));

	//connect(this->m_ui->tableWidget, SIGNAL(itemChanged(QTableWidgetItem*)), this, SLOT(cell_valueChanged(QTableWidgetItem*)));
	//connect(this->m_ui->tableWidget, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), this, SLOT(cell_DblClicked(QTableWidgetItem*)));

	connect(this->m_ui->wUpBarProps, SIGNAL(updateClicked()), this, SLOT(refreshSmart()));
}

wCore_PropertiesProvider::~wCore_PropertiesProvider()
{
	delete this->m_ui;
	if ( this->menuKey != NULL )
		delete this->menuKey;
	if ( this->menuValue != NULL )
		delete this->menuValue;
	if ( this->menuPerm != NULL )
		delete this->menuPerm;
}

void wCore_PropertiesProvider::changeEvent(QEvent *e) {
	QWidget::changeEvent(e);
	if ( e->type() != QEvent::LanguageChange ) return;
	this->m_ui->retranslateUi(this);
	refreshData(true);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void wCore_PropertiesProvider::buildMenus() {
	this->menuTitle = new QAction(this);
	this->menuTitle->setEnabled(false);
	this->menuTitle->setIcon( GuiUtilities::createIconFor(Property::className()) );

	this->actionKeyRefresh = new QAction(this);
	this->actionKeyRefresh->setText(tr("&Refresh"));
	this->actionKeyMan = new QAction(this);
	this->actionKeyMan->setText(tr("Man.."));
	this->actionKeyCopyTo = new QAction(this);
	this->actionKeyCopyTo->setText(tr("Copy To (new).."));
	this->actionKeyDelete = new QAction(this);
	this->actionKeyDelete->setText(tr("&Delete"));
	this->actionKeyNew = new QAction(this);
	this->actionKeyNew->setText(tr("New Property.."));
	this->menuKey = new QMenu(this);
	this->menuKey->addAction(this->menuTitle);
	this->menuKey->addSeparator();
	this->menuKey->addAction(this->actionKeyRefresh);
	this->menuKey->addAction(this->actionKeyMan);
	this->menuKey->addSeparator();
	this->menuKey->addAction(this->actionKeyCopyTo);
	this->menuKey->addAction(this->actionKeyDelete);
	this->menuKey->addSeparator();
	this->menuKey->addAction(this->actionKeyNew);

	this->actionValueRefresh = new QAction(this);
	this->actionValueRefresh->setText(tr("&Refresh Value"));
	this->actionValueEdit = new QAction(this);
	this->actionValueEdit->setText(tr("&Edit.."));
	this->menuValue = new QMenu(this);
	this->menuValue->addAction(this->menuTitle);
	this->menuValue->addSeparator();
	this->menuValue->addAction(this->actionValueRefresh);
	this->menuValue->addAction(this->actionValueEdit);

	this->actionPermRefresh = new QAction(this);
	this->actionPermRefresh->setText(tr("&Refresh Permission"));
	this->actionPermEdit = new QAction(this);
	this->actionPermEdit->setText(tr("&Edit.."));
	this->menuPerm = new QMenu(this);
	this->menuPerm->addAction(this->menuTitle);
	this->menuPerm->addSeparator();
	this->menuPerm->addAction(this->actionPermRefresh);
	this->menuPerm->addAction(this->actionPermEdit);
 }

void wCore_PropertiesProvider::contextMenuEvent( QContextMenuEvent* menu_event )
 {
	QModelIndex index = this->m_ui->tableView->getTableView()->indexAt( menu_event->pos() );
	if ( !index.isValid() || index.row() == 0 ) return;
	if ( this->menuKey == NULL ) buildMenus();

	Variant propKey = this->m_ui->tableView->getTableView()->getItemAsVariant( index.row()-1, 0 );
	this->menuTitle->setText( propKey.toString().c_str() );

	QAction* selected = NULL;
	switch( index.column() ) {
		case 0:		selected = this->menuKey->exec( menu_event->globalPos() ); break;
		case 1:		selected = this->menuValue->exec( menu_event->globalPos() ); break;
		case 2:		selected = this->menuPerm->exec( menu_event->globalPos() ); break;
		default:	return;
	 }

	if ( selected == this->actionKeyRefresh || selected == this->actionValueRefresh || selected == this->actionPermRefresh ) {
		// not optimized, always get the whole Property object
		executeRemoteMethod( _METHOD_GetProperty, propKey.clone() );
		//message->setDestinationBus( _Bus_ANY );
	 }
	else if ( selected == this->actionKeyNew ) {
		// open new widget
		//WidgetProperty* wProp = new WidgetProperty();
		//wProp->show();
		//wProp->deleteLater();
	 }
	else if ( selected == this->actionKeyCopyTo ) {
		QString text = QInputDialog::getText(this, tr("Copy Property"), tr("Copy property '%1' to (new Key):").arg(propKey.toString().c_str()) );
		if ( text.isEmpty() ) return;
		Variant propValue = this->m_ui->tableView->getTableView()->getItemAsVariant( index.row()-1, 1 );
		const Permission* perm = this->m_ui->tableView->getTableView()->getItem<Permission>( index.row()-1, 2 );

		Property* prop = new Property(text.toStdString(), propValue);
		if ( perm != NULL )
			prop->setPermission(*perm);
		else {
			prop->setPermission( Permission( GuiSession::getInstance().getCurrentKernel(), "nobody", Permission::RWX, Permission::Read, Permission::None) );
		 }
		executeRemoteMethod( _METHOD_SetProperty, prop );
	 }
	else if ( selected == this->actionKeyMan ) {
		executeRemoteMethod( _METHOD_Man, propKey.clone() );
	 }
	else if ( selected == this->actionKeyDelete ) {
		executeRemoteMethod( _METHOD_DelProperty, propKey.clone() );
	 }
	else if ( selected == this->actionValueEdit ) {
		Variant propValue = this->m_ui->tableView->getTableView()->getItemAsVariant( index.row()-1, 1 );
	 }
	else if ( selected == this->actionPermEdit ) {
		//const Permission* perm = this->m_ui->tableView->getTableView()->getItem<Permission>( index.row()-1, 2 );
		// open new widget
		//WidgetPermission* wPerm = new WidgetPermission();
		//wPerm->show();
		//wPerm->deleteLater();
	 }
	

 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void wCore_PropertiesProvider::setTargetURI( const URI& uri )
{
	IWidget::setTargetURI( uri );
	this->m_ui->wUpBarProps->setEnabled(this->getTargetURI().isValid());
}

WRESULT wCore_PropertiesProvider::reset()
 {
	IWidget::setTargetURI( URI() );
	this->m_ui->wUpBarProps->setEnabled( false );

	this->m_ui->tableView->getTableView()->loadObject(NULL);
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void wCore_PropertiesProvider::refreshData( bool force_all )
 {
	this->m_ui->wUpBarProps->setEnabled( this->getTargetURI().isValid() );
	if ( !this->getTargetURI().isValid() ) {
		return;
	 }
	Request* request_properties = NULL;
	if ( !force_all && this->m_ui->tableView->getTableModel() != NULL && this->m_ui->tableView->getTableModel()->getDataTable() != NULL && this->m_ui->tableView->getTableView()->selectionModel()->hasSelection() ) {
		List* keys_list = new List();
		QModelIndexList iList = this->m_ui->tableView->getTableView()->selectionModel()->selectedIndexes();
		unsigned int iH = 0;
		for(int i=0; i<iList.size(); i++) {
			wosh::Variant dataName = this->m_ui->tableView->getTableView()->getItemAsVariant( iList.at(i).row()+iH, 0);
			if ( keys_list->search(&dataName) != -1 )
				continue;
			keys_list->push_back(dataName.clone());
		 }
		if ( keys_list->size() == 1 ) {
			request_properties = new Request( _METHOD_GetProperty, keys_list->itemAt(0)->clone() );
			this->m_ui->wUpBarProps->showMessage( QString(tr("Retrieving property '%1'..")).arg(keys_list->itemAt(0)->toString().c_str()) );
			delete keys_list;
		 }
		else if ( keys_list->size() != 0 && keys_list->size() < this->m_ui->tableView->getTableView()->model()->rowCount() ) {
			request_properties = new Request( _METHOD_GetProperties, keys_list );
			this->m_ui->wUpBarProps->showMessage( QString(tr("Retrieving %1 properties..")).arg(keys_list->size()) );
		 }
	 }

	if ( request_properties == NULL ) {
		request_properties = new Request( _METHOD_ListProperties );
		this->m_ui->wUpBarProps->sayRetrieving();
	 }

	Message* message = new Message(request_properties);
	message->setDestinationBus( _Bus_ANY );
	postMessageForTarget(message);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT wCore_PropertiesProvider::loadObject( const wosh::PropertiesProvider* obj ) {

	return WRET_OK;
 }

WRESULT wCore_PropertiesProvider::saveObject( wosh::PropertiesProvider* obj ) const {
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT wCore_PropertiesProvider::loadTable( const Table* table ) {
	this->m_ui->tableView->getTableModel()->loadTable(table);
	this->m_ui->tableView->getTableView()->clearSelection();
	this->m_ui->tableView->getTableView()->resizeColumnsToContents();
	this->m_ui->tableView->getTableView()->horizontalHeader()->setResizeMode(1,QHeaderView::Stretch);
	this->m_ui->wUpBarProps->sayReady();
	return WRET_OK;
 }

WRESULT wCore_PropertiesProvider::updateTable( const Table* table ) {
	if ( table == NULL ) return WRET_ERR_PARAM;
	this->m_ui->wUpBarProps->showMessage( QString(tr("Parsing Data [%1 rows]..")).arg(table->getRows()) );
	this->m_ui->tableView->getTableView()->loadObject(table);
	this->m_ui->tableView->getTableView()->clearSelection();
	this->m_ui->tableView->getTableView()->resizeColumnsToContents();
	this->m_ui->tableView->getTableView()->horizontalHeader()->setResizeMode(1,QHeaderView::Stretch);
	this->m_ui->wUpBarProps->sayReady();
	return WRET_OK;
 }


//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void wCore_PropertiesProvider::busMessage( const Message& message, const Bus* ) {
	if ( message.isEmpty() ) return;
	if ( !message.getContent()->isResponse() ) return;
	if ( !isMessageForMeOrFromTarget(message) )
		return;

	const wosh::Response* response = message.getContent()->asResponse();
	if ( WFAILED(response->getReturnValue()) ) {
		if ( !URI::pregMatch( this->myURI, message.getDestination() ) )
			return;
		QString message = "Internal Error";

		if ( response->getMethod() == _METHOD_ListProperties )
			message = tr("Error#%1 retrieving Properties (Table)!").arg(response->getReturnValue());
		else if ( response->getMethod() == _METHOD_GetProperty )
			message = tr("Error#%1 retrieving Property!").arg(response->getReturnValue());
		else if ( response->getMethod() == _METHOD_SetProperty )
			message = tr("Error#%1 setting Property!").arg(response->getReturnValue());
		else if ( response->getMethod() == _METHOD_DelProperty )
			message = tr("Error#%1 deleting Property!").arg(response->getReturnValue());
		else if ( response->getMethod() == _METHOD_GetProperties )
			message = tr("Error#%1 retrieving Properties!").arg(response->getReturnValue());
		else if ( response->getMethod() == _METHOD_Man )
			message = tr("Error#%1 retrieving Man!").arg(response->getReturnValue());

		if ( response->hasData() && response->getData()->isKindOf<Variant>() ) {
			const wosh::Variant* dataInfo = dynamic_cast<const wosh::Variant*>(response->getData());
			message += QString(" [%1]").arg(dataInfo->toString().c_str());
		 }
		this->m_ui->wUpBarProps->showError(message);
		return;
	 }
	if ( !response->hasData() )
		return;

	if ( response->getMethod() == _METHOD_ListProperties ) {
		if ( response->getData()->isKindOf<Table>() ) {
			const wosh::Table* propTable = response->getData()->as<Table>();
			loadTable(propTable);
		 }
	 }
	else if ( response->getMethod() == _METHOD_GetProperty || response->getMethod() == _METHOD_SetProperty ) {

		if ( response->getData()->isKindOf<Property>() ) {
			const wosh::Property* prop = response->getData()->as<Property>();
			Table* table = new Table(1,3);
			table->set<Variant>( prop->getKey(), 0,0);
			table->set( prop->getValue().clone(), 0,1);
			table->set( prop->getPermission().clone(), 0,2);
			updateTable(table);
			delete table;
			if ( response->getMethod() == _METHOD_SetProperty )
				this->m_ui->wUpBarProps->showMessage( QString(tr("Applied Property '%1'")).arg(prop->getKey().c_str()) );
		 }
	 }
	else if ( response->getMethod() == _METHOD_GetProperties ) {

		if ( response->getData()->isKindOf<Table>() ) {
			const wosh::Table* propTable = response->getData()->as<Table>();
			updateTable(propTable);
		 }
	 }
	else if ( response->getMethod() == _METHOD_Man ) {
		if ( !response->hasData() )
			return;
		QMessageBox::information(this, "Man", response->getData()->toString().c_str() );
	 }

 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void wCore_PropertiesProvider::cell_valueChanged( unsigned int row, unsigned int col, ISerializable* newValue )
 {
	if ( newValue == NULL )
		return;
	wosh::Variant nameData = this->m_ui->tableView->getTableView()->getItemAsVariant(row, 0 );
	Property* new_property = new Property(nameData.toString());

	if ( col == 1 ) {
		if ( !newValue->isKindOf<Variant>() ) {
			return;
		 }
		const wosh::Variant* valueData = dynamic_cast<const wosh::Variant*>(newValue);
		new_property->setValue(*valueData);
		// dont set permission (so it is empty), so it wont be updated!
	 }
	else if ( col == 2 ) {
		if ( !newValue->isKindOf<Permission>() ) {
			return;
		 }
		const wosh::Permission* propPerm = dynamic_cast<const wosh::Permission*>(newValue);
		new_property->setPermission(*propPerm);
		// dont set value (so it is empty), so it wont be updated!
		new_property->getValue().clear();
	 }

	Request* request_properties = new Request( _METHOD_SetProperty, new_property );
	Message* message = new Message(request_properties);
	message->setDestinationBus( _Bus_ANY );
	postMessageForTarget(message);
	this->m_ui->wUpBarProps->showMessage( QString(tr("Applying changes of '%1'..")).arg(nameData.toString().c_str()) );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace gui
}; // namespace wosh
